﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.ExpressionEvaluation
{
    /// <summary>
    /// This class represents a parser for a stack based math notation. Notationexample: 1 2 + 3 - sin.
    /// </summary>
    [Serializable]
    public class StackNotationParser
    {
        /// <summary>
        /// An internal pointer for holding the token index.
        /// </summary>
        private int pointer;

        /// <summary>
        /// The math expression to parse.
        /// </summary>
        private readonly String value;

        /// <summary>
        /// The tokens of the math expression to parse.
        /// </summary>
        private readonly String[] tokens;

        /// <summary>
        /// Initializes a new instance of the <see cref="StackNotationParser"/> class.
        /// </summary>
        /// <param name="value">The math expression to parse.</param>
        public StackNotationParser(String value)
        {
            if (value == (string) null)
            {
                throw new ArgumentNullException("value");
            }

            if (value.Length == 0)
            {
                throw new ArgumentException("value.Length == 0", "value");
            }

            this.pointer = 0;
            this.value = value.Trim();
            this.tokens = value.Trim().Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        }

        /// <summary>
        /// Gets the math expression to parse.
        /// </summary>
        /// <value>The math expression to parse.</value>
        public string Value
        {
            get { return value; }
        }

        /// <summary>
        /// Gets the current token index.
        /// </summary>
        /// <value>The current token index.</value>
        public int TokenIndex
        {
            get { return this.pointer; }
        }

        /// <summary>
        /// Gets a value indicating whether the expression has more tokens.
        /// </summary>
        /// <value>
        /// True if the expression has more tokens otherwise, false.
        /// </value>
        public bool HasMoreTokens
        {
            get { return this.pointer < (this.tokens.Length - 1); }
        }

        /// <summary>
        /// Gets a value indicating whether the expression has a next token.
        /// </summary>
        /// <value>
        /// True if the expression has a next token otherwise, false.
        /// </value>
        public bool HasNextTokens
        {
            get { return (this.pointer + 1) < this.tokens.Length; }
        }

        /// <summary>
        /// Gets a value indicating whether the expression has a previous token.
        /// </summary>
        /// <value>
        /// True if the expression has a previous token otherwise, false.
        /// </value>
        public bool HasPreviousTokens
        {
            get { return (this.pointer - 1) >= 0; }
        }

        /// <summary>
        /// Gets a value indicating whether the expression has a penultimate token.
        /// </summary>
        /// <value>
        /// True if the expression has a penultimate token otherwise, false.
        /// </value>
        public bool HasPenultimateTokens
        {
            get { return (this.pointer - 2) >= 0; }
        }

        /// <summary>
        /// Gets the next token of the specified expression.
        /// </summary>
        /// <returns>The next token of the specified expression.</returns>
        public String NextToken()
        {
            if (this.pointer < this.tokens.Length)
            {
                return this.tokens[this.pointer + 1];
            }

            throw new TokenDoesNotExistException("The requested token does not exist.");
        }

        /// <summary>
        /// Gets the previous token of the specified expression.
        /// </summary>
        /// <returns>The previous token of the specified expression.</returns>
        public String PreviousToken()
        {
            if ((this.pointer) > 0)
            {
                return this.tokens[this.pointer - 1];
            }

            throw new TokenDoesNotExistException("The requested token does not exist.");
        }

        /// <summary>
        /// Gets the penultimate token of the specified expression.
        /// </summary>
        /// <returns>The penultimate token of the specified expression.</returns>
        public String PenultimateToken()
        {
            if ((this.pointer) > 0)
            {
                return this.tokens[this.pointer - 2];
            }

            throw new TokenDoesNotExistException("The requested token does not exist.");
        }

        /// <summary>
        /// Gets the current token of the specified expression.
        /// </summary>
        /// <returns>The current token of the specified expression.</returns>
        public String CurrentToken()
        {
            return this.tokens[this.pointer];
        }

        /// <summary>
        /// Shifts the expression to the left, so that the current token becomes the next token.
        /// </summary>
        public void ShiftLeft()
        {
            this.pointer--;
        }

        /// <summary>
        /// Shifts the expression to the right, so that the current token becomes the previous token.
        /// </summary>
        public void ShiftRight()
        {
            this.pointer++;
        }

        /// <summary>
        /// Resets the token index of the parser to zero.
        /// </summary>
        public void Reset()
        {
            this.pointer = 0;
        }
    }
}